﻿using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Text;
using System.Net;
using System.IO;
using System.Collections;
using System.Linq;
namespace Framework.Common
{
    /// <summary>
    /// HttpWebRequest HttpWebResponse Helper
    /// </summary>
    public static class HttpWebHelper
    {
        #region core
        /// <summary>
        /// 获取内容
        /// </summary>
        /// <param name="rp">RequestParameter</param>
        /// <returns></returns>
        public static bool GetHtml(RequestParameter rp)
        {
            try
            {
                var hwResponse = CreateResponse(rp);
                if (hwResponse == null) return false;
                byte[] bytes = null;
                Encoding encoding = CheckEncoding(hwResponse, ref bytes) ?? rp.Encoding;

                #region 判断是否和页面声明的编码相同

                rp.Text = encoding.GetString(bytes);
                var declareEncodeName = rp.Text.RegularTest("<\\s*?meta\\s+?.*?charset\\s*?=(?:\\s*|\"|')([a-z0-9_-]+)(?:\"|'|;|\\s*).*?>", 1);
                if (declareEncodeName.IsBlank()) return true;
                try
                {
                    Encoding declareEncoding = Encoding.GetEncoding(declareEncodeName);
                    if (Equals(encoding, declareEncoding)) return true;
                    rp.Text = declareEncoding.GetString(bytes);
                    return true;
                }
                catch (Exception ex)
                {
                    LogHelper.Error("编码转化失败--url:" + rp.Url, ex);
                    return true;
                }
                #endregion
            }
            catch (Exception ex)
            {
                LogHelper.Fatal("获取web数据错误--url:" + rp.Url, ex);
                return false;
            }
        }

        /// <summary>
        /// 获取内容
        /// </summary>
        /// <param name="rp">RequestParameter</param>
        /// <returns></returns>
        public static bool GetBytes(RequestParameter rp)
        {
            try
            {
                var hwResponse = CreateResponse(rp);
                if (hwResponse == null) return false;

                using (var stream = hwResponse.GetResponseStream())
                {
                    rp.Bytes = stream.ToBytes();
                    return true;
                }
            }
            catch (Exception ex)
            {
                LogHelper.Fatal("获取web数据错误--url:" + rp.Url, ex);
                return false;
            }
        }

        /// <summary>
        /// CreateResponse by RequestParameter
        /// </summary>
        /// <param name="rp"></param>
        /// <returns></returns>
        private static HttpWebResponse CreateResponse(RequestParameter rp)
        {
            try
            {
                //构建即将发送的包头         
                var hwRequest = (HttpWebRequest)WebRequest.Create(new Uri(rp.Url));

                hwRequest.AllowAutoRedirect = rp.AllowAutoRedirect;

                if (rp.TimeOut > 0)
                {
                    hwRequest.Timeout = rp.TimeOut;
                }
                if (rp.InitCookie || rp.CookieCollection != null)
                {
                    if (hwRequest.CookieContainer == null)
                    {
                        hwRequest.CookieContainer = new CookieContainer();
                    }
                }
                if (rp.CookieCollection != null)
                {
                    hwRequest.CookieContainer.Add(rp.CookieCollection);
                }
                if (!rp.Accept.IsBlank())
                {
                    hwRequest.Accept = rp.Accept;
                }
                if (!rp.ContentType.IsBlank())
                {
                    hwRequest.ContentType = rp.ContentType;
                }
                if (!rp.UserAgent.IsBlank())
                {
                    hwRequest.UserAgent = rp.UserAgent;
                }
                if (!rp.Method.IsBlank())
                {
                    hwRequest.Method = rp.Method;
                }
                hwRequest.ServicePoint.Expect100Continue = rp.Expect100Continue;
                if (rp.PostData != null)
                {
                    hwRequest.ContentLength = rp.PostData.Length;
                    //写入post信息  
                    using (Stream streamRequest = hwRequest.GetRequestStream())
                    {
                        streamRequest.Write(rp.PostData, 0, rp.PostData.Length);
                    }
                }
                return hwRequest.GetResponse() as HttpWebResponse;
            }
            catch (Exception ex)
            {
                LogHelper.Fatal("创建请求出错--url:" + rp.Url, ex);
                return null;
            }
        }

        #endregion

        #region check Encoding

        /// <summary>
        /// 检测编码,并返回Stream的byte[]
        /// </summary>
        /// <param name="resp"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static Encoding CheckEncoding(HttpWebResponse resp, ref byte[] bytes)
        {
            using (var stream = resp.GetResponseStream())
            {
                if (stream != null)
                {
                    if (!resp.ContentEncoding.IsBlank())
                    {
                        #region gzip
                        if (resp.ContentEncoding.ToLower() == "gzip")
                        {
                            using (var gZipStream = new GZipStream(stream, CompressionMode.Decompress))
                            {
                                bytes = gZipStream.ToBytes();
                            }
                        }
                        #endregion

                        #region deflate
                        else if (resp.ContentEncoding.ToLower() == "deflate")
                        {
                            using (var deflateStream = new DeflateStream(stream, CompressionMode.Decompress))
                            {
                                bytes = deflateStream.ToBytes();
                            }
                        }
                        #endregion

                        #region no content encoding
                        else
                        {
                            bytes = stream.ToBytes();
                        }
                        #endregion
                    }
                    else
                    {
                        bytes = stream.ToBytes();
                    }
                }
            }

            if (resp.CharacterSet.IsNotBlank())//根据Content-Type中获取的charset编码  
            {
                if (string.Compare(resp.CharacterSet, "ISO-8859-1", true, System.Globalization.CultureInfo.InvariantCulture) == 0)
                {
                    return Encoding.GetEncoding("GB2312");
                }
                return Encoding.GetEncoding(resp.CharacterSet);
            }
            return null;
        }

        #endregion

        #region post data

        /// <summary>  
        /// 根据泛型来构建字符串用于post  
        /// </summary>
        /// <returns>构建完毕的字符串</returns>  
        public static byte[] CreatePostData(Dictionary<string, string> dict)
        {
            string data = string.Join("&", dict.Select(i => i.Key + "=" + i.Value).ToArray());
            return CreatePostData(data);
        }

        /// <summary>
        /// string to byte[]
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static byte[] CreatePostData(string input)
        {
            return Encoding.Default.GetBytes(input);
        }
        #endregion

        #region string to cookieCollection

        //httphead 字符串如下
        /*
        HTTP/1.1 302 Moved Temporarily
        Transfer-Encoding: chunked
        Date: Wed, 10 Jun 2009 13:22:53 GMT
        Content-Type: text/html; charset=UTF-8
        Expires: Wed, 10 Jun 2009 13:22:53 GMT
        Cache-Control: no-cache, must-revalidate, max-age=0
        Server: nginx/0.7.41
        X-Powered-By: PHP/5.2.9
        Last-Modified: Wed, 10 Jun 2009 13:22:52 GMT
        Pragma: no-cache
        Set-Cookie: cookie1=c1; path=/; domain=site.com
        Set-Cookie: cookie2=c2; path=/content; domain=site.com; httponly
        Set-Cookie: cookie3=c3; path=/admin; domain=site.com; httponly
        Set-Cookie: cookie4=c4; path=/; domain=site.com; httponly
        Location: http://site.com/admin/
        Via: 1.1 mvo-netcache-02 (NetCache NetApp/6.0.7)
        */

        /// <summary>
        /// 将head cookie 字符转为 CookieCollection
        /// </summary>
        /// <param name="strHeader"></param>
        /// <param name="strHost"></param>
        /// <returns></returns>
        public static CookieCollection GetAllCookiesFromHeader(string strHeader, string strHost)
        {
            var cc = new CookieCollection();
            if (strHeader != string.Empty)
            {
                ArrayList al = ConvertCookieHeaderToArrayList(strHeader);
                cc = ConvertCookieArraysToCookieCollection(al, strHost);
            }
            return cc;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="strCookHeader"></param>
        /// <returns></returns>
        private static ArrayList ConvertCookieHeaderToArrayList(string strCookHeader)
        {
            strCookHeader = strCookHeader.Replace("\r", "");
            strCookHeader = strCookHeader.Replace("\n", "");
            string[] strCookTemp = strCookHeader.Split(',');
            var al = new ArrayList();
            int i = 0;
            int n = strCookTemp.Length;
            while (i < n)
            {
                if (strCookTemp[i].IndexOf("expires=", StringComparison.OrdinalIgnoreCase) > 0)
                {
                    al.Add(strCookTemp[i] + "," + strCookTemp[i + 1]);
                    i = i + 1;
                }
                else
                {
                    al.Add(strCookTemp[i]);
                }
                i = i + 1;
            }
            return al;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="al"></param>
        /// <param name="strHost"></param>
        /// <returns></returns>
        private static CookieCollection ConvertCookieArraysToCookieCollection(ArrayList al, string strHost)
        {
            var cc = new CookieCollection();

            int alcount = al.Count;
            for (int i = 0; i < alcount; i++)
            {
                string strEachCook = al[i].ToString();
                string[] strEachCookParts = strEachCook.Split(';');
                int intEachCookPartsCount = strEachCookParts.Length;
                var cookTemp = new Cookie();

                for (int j = 0; j < intEachCookPartsCount; j++)
                {
                    if (j == 0)
                    {
                        string strCNameAndCValue = strEachCookParts[j];
                        if (strCNameAndCValue != string.Empty)
                        {
                            int firstEqual = strCNameAndCValue.IndexOf("=");
                            string firstName = strCNameAndCValue.Substring(0, firstEqual);
                            string allValue = strCNameAndCValue.Substring(firstEqual + 1, strCNameAndCValue.Length - (firstEqual + 1));
                            cookTemp.Name = firstName;
                            cookTemp.Value = allValue;
                        }
                        continue;
                    }
                    string strPNameAndPValue;
                    string[] nameValuePairTemp;
                    if (strEachCookParts[j].IndexOf("path", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        strPNameAndPValue = strEachCookParts[j];
                        if (strPNameAndPValue != string.Empty)
                        {
                            nameValuePairTemp = strPNameAndPValue.Split('=');
                            if (nameValuePairTemp[1] != string.Empty)
                            {
                                cookTemp.Path = nameValuePairTemp[1];
                            }
                            else
                            {
                                cookTemp.Path = "/";
                            }
                        }
                        continue;
                    }

                    if (strEachCookParts[j].IndexOf("domain", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        strPNameAndPValue = strEachCookParts[j];
                        if (strPNameAndPValue != string.Empty)
                        {
                            nameValuePairTemp = strPNameAndPValue.Split('=');

                            if (nameValuePairTemp[1] == string.Empty)
                            {
                                cookTemp.Domain = strHost;
                            }
                            else
                            {
                                cookTemp.Domain = nameValuePairTemp[1];
                            }
                        }
                    }
                }

                if (cookTemp.Path == string.Empty)
                {
                    cookTemp.Path = "/";
                }
                if (cookTemp.Domain == string.Empty)
                {
                    cookTemp.Domain = strHost;
                }
                cc.Add(cookTemp);
            }
            return cc;
        }
        #endregion
    }

    #region Requestparam
    /// <summary>  
    /// httpwebrequest类中的一些属性的集合  
    /// </summary>  
    public class RequestParameter
    {
        const string DefaultContentType = "application/x-www-form-urlencoded";
        const string FormContentType = "multipart/form-data";

        #region property
        /// <summary>
        /// Referer
        /// </summary>
        public string Referer { get; set; }

        /// <summary>
        /// 请求的URL
        /// </summary>
        public string Url { get; set; }

        /// <summary>  
        /// 获取或设置request类中的Accept属性  
        /// 用以设置接受的文件类型  
        /// </summary>              
        public string Accept { get; set; }

        /// <summary>  
        /// 获取或设置request类中的ContentType属性  
        /// 用以设置请求的媒体类型  
        /// </summary>       
        string contentType = DefaultContentType;
        public string ContentType
        {
            get
            {
                return contentType;
            }
            set
            {
                if (!contentType.IsBlank())
                    contentType = value;
            }
        }

        /// <summary>  
        /// 获取或设置request类中的UserAgent属性  
        /// 用以设置请求的客户端信息  
        /// </summary>  
        public string UserAgent { get; set; }

        /// <summary>  
        /// 获取或设置request类中的Method属性  
        /// 可以将 Method 属性设置为任何 HTTP 1.1 协议谓词：GET、HEAD、POST、PUT、DELETE、TRACE 或 OPTIONS。  
        /// 如果 ContentLength 属性被设置为 -1 以外的任何值，则必须将 Method 属性设置为上载数据的协议属性。  
        /// </summary>    
        string method = "GET";
        public string Method
        {
            get
            {
                return method;
            }
            set
            {
                method = value;
            }
        }

        byte[] postData;
        /// <summary>
        /// 发送的数据
        /// </summary>
        public byte[] PostData
        {
            get
            {
                return postData;
            }
            set
            {
                postData = value;
                if (postData != null)
                {
                    method = "POST";
                    if (contentType.IndexOf(DefaultContentType) < 0 && contentType.IndexOf(FormContentType) < 0)
                    {
                        contentType = contentType + ";" + DefaultContentType;
                    }
                }
            }
        }

        /// <summary>
        /// 发送的数据 a=b&d=e
        /// </summary>
        public string PostDataString
        {
            get;
            set;
        }

        /// <summary>
        /// CookieCollection(从GetCookie方法返回的CookieCollection)
        /// </summary>
        public CookieCollection CookieCollection { get; set; }

        /// <summary>
        /// 请求超时时间
        /// </summary>
        public int TimeOut { get; set; }


        Encoding encoding = Encoding.Default;
        /// <summary>
        /// 请求的编码
        /// </summary>
        public Encoding Encoding
        {
            get
            {
                return encoding;
            }
            set
            {
                encoding = value;
            }
        }

        /// <summary>
        /// 是否做初始化cookie的操作
        /// </summary>
        public bool InitCookie { get; set; }

        /// <summary>
        /// 网页的内容
        /// </summary>
        public string Text { get; set; }

        /// <summary>
        /// 非 html 返回的字节流
        /// </summary>
        public byte[] Bytes { get; set; }

        /// <summary>
        /// 是否允许自动转向
        /// </summary>
        public bool AllowAutoRedirect { get; set; }

        /// <summary>
        /// 请求的IP地址
        /// </summary>
        public string RequestIp { get; set; }


        private bool expect100Continue = false;
        public bool Expect100Continue
        {
            get { return expect100Continue; }
            set { expect100Continue = value; }
        }

        #endregion

        #region method
        /// <summary>
        /// 设置PostData
        /// </summary>
        /// <param name="data"></param>
        public void SetPostData(string data)
        {
            PostData = HttpWebHelper.CreatePostData(data);
        }

        /// <summary>
        /// 设置PostData
        /// </summary>
        /// <param name="data"></param>
        public void SetPostData(Dictionary<string, string> data)
        {
            PostData = HttpWebHelper.CreatePostData(data);
        }
        #endregion
    }
    #endregion
}
